﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows;
using System.Windows.Media.Imaging;
using DaveSexton.Labs.Properties;

namespace DaveSexton.Labs
{
	public class WindowsLabController : LabController
	{
		#region Public Properties
		public ILabHost Host
		{
			get
			{
				Contract.Ensures(Contract.Result<ILabHost>() != null);

				return host;
			}
		}
		#endregion

		#region Private / Protected
		internal static NativeWindow CurrentNativeWindow
		{
			get;
			private set;
		}

		private static readonly Uri windowIcon = new Uri("pack://application:,,,/DaveSexton.Labs;component/Images/Labs.ico");

		private readonly ILabHost host;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="WindowsLabController" /> class.
		/// </summary>
		public WindowsLabController()
			: this(new WindowsLabHost())
		{
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="WindowsLabController" /> class.
		/// </summary>
		public WindowsLabController(ILabHost host)
		{
			Contract.Requires(host != null);

			this.host = host;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="WindowsLabController" /> class.
		/// </summary>
		public WindowsLabController(LabCatalog catalog)
			: this(new WindowsLabHost(), catalog)
		{
			Contract.Requires(catalog != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="WindowsLabController" /> class.
		/// </summary>
		public WindowsLabController(ILabHost host, LabCatalog catalog)
			: base(catalog)
		{
			Contract.Requires(host != null);
			Contract.Requires(catalog != null);

			this.host = host;
		}
		#endregion

		#region Methods
		[ContractInvariantMethod]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
		private void ObjectInvariant()
		{
			Contract.Invariant(host != null);
		}

		public virtual void Show()
		{
			Show(new Window());
		}

		public virtual void Show(ComposablePartCatalog discoveryCatalog)
		{
			Contract.Requires(discoveryCatalog != null);

			Show(new Window(), discoveryCatalog);
		}

		public virtual void Show(Window window)
		{
			Contract.Requires(window != null);

			// Must show the window before starting the controller to ensure that the XAML template is loaded.
			ShowWindow(window);

			// Must start the controller after assigning the host to the window so that it displays the welcome info.
			Start();

			// Must assign the title after starting the controller so that the value is not an empty string.
			window.Title = host.ApplicationTitle;
		}

		public virtual void Show(Window window, ComposablePartCatalog discoveryCatalog)
		{
			Contract.Requires(window != null);
			Contract.Requires(discoveryCatalog != null);

			// Must show the window before starting the controller to ensure that the XAML template is loaded.
			ShowWindow(window);

			// Must start the controller after assigning the host to the window so that it displays the welcome info.
			Start(discoveryCatalog);

			// Must assign the title after starting the controller so that the value is not an empty string.
			window.Title = host.ApplicationTitle;
		}

		private void ShowWindow(Window window)
		{
			Contract.Requires(window != null);

			window.Content = host;

			if (window.Icon == null)
			{
				window.Icon = new BitmapImage(windowIcon);
			}

			WindowSettings.Default.Track(window);

			CurrentNativeWindow = new NativeWindow(window);

			window.Show();
		}

		protected override void ZeroLabs()
		{
			host.ApplicationTitle = ApplicationTitle;
		}

		protected override void MainMenu(ICollection<Lab> labs)
		{
			foreach (var lab in labs)
			{
				Contract.Assume(lab != null);
				Contract.Assume(lab.Listeners != null);

				lab.Listeners.Add(this);
			}

			host.ApplicationTitle = ApplicationTitle;

			host.LoadLabs(LabCategory.Organize(labs));

			var first = labs.FirstOrDefault();

			if (host.CanChangeLab && first != null)
			{
				host.SetCurrentLab(first);
			}
		}

		protected override void EndDebug(ICollection<Lab> labs)
		{
			// do nothing
		}

		protected override void TraceLabDescriptionIf(bool trace, string description)
		{
			if (trace)
			{
				TraceInformation(description);
			}
		}

		public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
		{
			if (eventType == TraceEventType.Critical)
			{
				WriteLine(Format(Resources.TraceEventFormat, eventType, message), eventType);
			}
			else
			{
				WriteLine(message, eventType);
			}
		}

		public sealed override void Write(string message, TraceEventType eventType)
		{
			host.Write(message, eventType);
		}

		public sealed override void WriteLine(string message, TraceEventType eventType)
		{
			host.WriteLine(message, eventType);
		}
		#endregion
	}
}